package com.googlecode.pacswitch;

import java.nio.channels.AsynchronousSocketChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

/**
 * @author Andrey Pavlenko
 */
@XmlJavaTypeAdapter(PStringAdapter.class)
public class PString {
    private static final Pattern _pattern = Pattern.compile("\\$\\{(.*)\\}");
    private final String _text;
    private final List<Unit> _units;

    public PString(final String text) {
        _text = text;
        _units = compile(text);
    }

    public String resolve(final PacSwitch ps,
            final AsynchronousSocketChannel clientSocket) {
        if (_units.size() == 1) {
            return _units.get(0).resolve(ps, clientSocket);
        } else {
            final StringBuilder sb = new StringBuilder();

            for (final Unit u : _units) {
                sb.append(u.resolve(ps, clientSocket));
            }

            return sb.toString();
        }
    }

    @Override
    public String toString() {
        return _text;
    }

    private static final List<Unit> compile(final String text) {
        final Matcher m = _pattern.matcher(text);
        final List<Unit> units = new ArrayList<Unit>();
        int lastOff = 0;

        while (m.find()) {
            final String name = m.group(1);
            final int start = m.start();
            final int end = m.end();

            if (start != lastOff) {
                units.add(new StringUnit(text.substring(lastOff, start)));
            }

            units.add(new PropertyUnit(name));
            lastOff = end;
        }

        if (text.length() != lastOff) {
            units.add(new StringUnit(text.substring(lastOff, text.length())));
        }

        switch (units.size()) {
        case 0:
            return Collections.<Unit> singletonList(new StringUnit(""));
        case 1:
            return Collections.<Unit> singletonList(units.get(0));
        default:
            return units;
        }
    }

    private static interface Unit {
        public String resolve(final PacSwitch ps,
                final AsynchronousSocketChannel clientSocket);
    }

    private static final class StringUnit implements Unit {
        private final String _text;

        public StringUnit(final String text) {
            _text = text;
        }

        @Override
        public String resolve(final PacSwitch ps,
                final AsynchronousSocketChannel clientSocket) {
            return _text;
        }

        @Override
        public String toString() {
            return _text;
        }
    }

    private static final class PropertyUnit implements Unit {
        private final String _name;

        public PropertyUnit(final String name) {
            _name = name;
        }

        @Override
        public String resolve(final PacSwitch ps,
                final AsynchronousSocketChannel clientSocket) {
            return ps.getConfig().getProperty(_name).resolve(ps, clientSocket);
        }

        @Override
        public String toString() {
            return "${" + _name + '}';
        }
    }
}
